﻿using MessageQueues.MSMQ.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.Text;
using System.Threading.Tasks;

namespace MessageQueues.MSMQ.Implement
{
    /// <summary>
    /// 消息队列控制器
    /// </summary>
    public partial class MessageQueueHandler : IMessageQueueMethod
    {
        #region 变量

        /// <summary>
        /// 是否停止循环接收消息数据
        /// </summary>
        public bool IsStopReceiveLoop { get; set; } = true;

        /// <summary>
        /// 消息队列对象
        /// </summary>
        private MessageQueue queue = null;

        /// <summary>
        /// 一个标志，标志是否初始化了获取完成数据的事件
        /// </summary>
        private bool initPeekAsync = false;

        /// <summary>
        /// 一个标志，标志是否初始化了接收完成数据的事件
        /// </summary>
        private bool initReceiveAsync = false;

        /// <summary>
        /// 消息队列的配置
        /// </summary>
        public MessageQueueSettings setting { get; set; }

        #endregion

        #region 构造函数

        /// <summary>
        /// 默认构造函数：不对外开放
        /// </summary>
        private MessageQueueHandler() { }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="setting">消息队列的配置</param>
        public MessageQueueHandler(MessageQueueSettings setting)
        {
            try
            {
                this.setting = setting;

                // 校验是否存在队列
                if (setting.RemotePath == "." && !Exists())
                    queue = Create(QueueType.PrivateQueue);
                else
                    queue = new MessageQueue(setting.MessageQueuePath, setting.AccessMode);

                if (setting.RemotePath == ".")
                {
                    // 设置是否经过身份验证：默认为否
                    queue.Authenticate = setting.Authenticate;

                    // 是否启用日志队列
                    queue.UseJournalQueue = setting.UseJournalQueue;

                    // 最大日志队列长度：最大值为uint.MaxValue
                    queue.MaximumJournalSize =
                        setting.MaximumJournalSize > uint.MaxValue ?
                        uint.MaxValue :
                        setting.MaximumJournalSize;

                    // 最大队列长度：最大值为uint.MaxValue
                    queue.MaximumQueueSize = setting.MaximumQueueSize > uint.MaxValue ? uint.MaxValue : setting.MaximumJournalSize;

                    // 队列标签
                    queue.Label = setting.MessageQueueLabel;


                }

                // 设置基优先级，默认是0
                queue.BasePriority = setting.BasePriority;

                // 获取消息队列的id
                setting.MessageQueueId = queue.Id;
            }
            catch (Exception)
            {
                throw new Exception("初始化消息队列处理器时出现异常");
            }

        }

        #endregion

        /// <summary>
        /// 同步获取队列消息
        /// </summary>
        /// <typeparam name="T">返回的数据类型</typeparam>
        /// <param name="action">获取数据后的回调</param>
        public void Receive<T>(Action<T> action) where T : class
        {
            try
            {
                queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
                Message message = queue.Receive();
                T obj = message.Body as T;
                if (obj != null)
                    action(obj);
                else
                    throw new InvalidCastException("队列获取的类型与泛型类型不符");
            }
            catch (Exception)
            {

                throw new Exception("同步获取队列消息时出现异常");
            }
        }

        /// <summary>
        /// 同步查询但不移除队列第一条数据
        /// </summary>
        /// <typeparam name="T">返回的数据类型</typeparam>
        /// <param name="action">获取数据后的回调</param>
        public void Peek<T>(Action<T> action) where T : class
        {
            try
            {
                queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
                Message message = queue.Peek();
                T obj = message.Body as T;
                if (obj != null)
                    action(obj);
                else
                    throw new InvalidCastException("队列获取的类型与泛型类型不符");
            }
            catch (Exception)
            {
                throw new Exception("同步查询队列数据时出现异常");
            }
        }

        /// <summary>
        /// 清空队列中包含的所有信息数据
        /// </summary>
        public void Purge()
        {
            try
            {
                queue.Purge();
            }
            catch (Exception)
            {
                throw new Exception("清空队列中包含的所有信息数据时出现异常");
            }

        }

        /// <summary>
        /// 发送数据到队列
        /// </summary>
        /// <typeparam name="T">发送的数据类型</typeparam>
        /// <param name="t">发送的数据对象</param>
        public void Send<T>(T t)
        {
            MessageQueueTransaction tran = null;
            try
            {
                tran = setting.IsUseTransaction ? new MessageQueueTransaction() : null;
                using (Message message = new Message())
                {
                    tran?.Begin();
                    message.Body = t;
                    message.Label = $"推送时间[{DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss.fff")}]";
                    message.UseDeadLetterQueue = true;
                    message.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
                    message.TimeToBeReceived = new TimeSpan(30, 0, 0, 0);
                    message.TimeToReachQueue = new TimeSpan(30, 0, 0, 0);
                    queue.Send(message, tran);
                    tran?.Commit();
                    tran?.Dispose();
                }
            }
            catch (Exception)
            {
                tran?.Abort();
                tran?.Dispose();
                throw new Exception("发送消息时出现异常");
            }
        }

        /// <summary>
        /// 获取所有消息
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <returns>取出来的数据</returns>
        public IList<T> GetAll<T>() where T : class
        {
            try
            {
                queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
                var enumerator = queue.GetMessageEnumerator2();
                var ret = new List<T>();
                while (enumerator.MoveNext())
                {
                    var messageInfo = enumerator.Current.Body as T;
                    if (messageInfo != null)
                    {
                        ret.Add(messageInfo);
                    }
                    enumerator.RemoveCurrent();
                    enumerator.Reset();
                }
                return ret;
            }
            catch (Exception)
            {
                return new List<T>();
            }
        }

        /// <summary>
        /// 发送多条数据
        /// </summary>
        /// <typeparam name="T">发送的数据类型</typeparam>
        /// <param name="ts">发送的数据对象集合</param>
        public void Send<T>(IList<T> ts)
        {
            MessageQueueTransaction tran = null;
            try
            {
                tran = setting.IsUseTransaction ? new MessageQueueTransaction() : null;
                tran?.Begin();
                foreach (var item in ts)
                {
                    using (Message message = new Message())
                    {
                        message.Body = item;
                        message.Label = $"推送时间[{DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss.fff")}]";
                        message.UseDeadLetterQueue = true;
                        message.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
                        message.TimeToBeReceived = new TimeSpan(30, 0, 0, 0);
                        message.TimeToReachQueue = new TimeSpan(30, 0, 0, 0);
                        queue.Send(message, tran);
                    }
                }
                tran?.Commit();
                tran?.Dispose();
            }
            catch (Exception)
            {
                tran?.Abort();
                tran?.Dispose();
                throw new Exception("发送消息时出现异常");
            }
        }
    }
}
